﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

namespace Framework.Manager
{
    public class LoadManager : ManagerBase<LoadManager>
    {
        #region Manager通用
        private LoadManager() { }
        #endregion

        public enum ELoadType
        {
            /// <summary>加载资源（单个）</summary>
            LoadAssetAsync,
            /// <summary>加载资源（多个）</summary>
            LoadAssetsAsyc,
            /// <summary>加载场景</summary>
            LoadSceneAsyc,
        }

        private Dictionary<int, AsyncOperationHandle> mHandleDictionay = new Dictionary<int, AsyncOperationHandle>();
        private Dictionary<int, Loader> mLoaderDictionary = new Dictionary<int, Loader>();

        #region 加载相关/Callback
        /// <summary>异步加载资源（单个）</summary>
        /// <param name="pAssetName">资源名称</param>
        /// <param name="pLoadCallback">加载回调</param>
        public void LoadAssetAsync(string pAssetName, Action<bool> pLoadCallback = null)
        {
            LoadAsset(pAssetName, ELoadType.LoadAssetAsync, pLoadCallback);
        }

        /// <summary>异步加载资源（多个）</summary>
        /// <param name="pLabelsName">Labals</param>
        /// <param name="pLoadCallback">加载回调</param>
        public void LoadAssetsAsyc(string pLabelsName, Action<bool> pLoadCallback = null)
        {
            LoadAsset(pLabelsName, ELoadType.LoadAssetsAsyc, pLoadCallback);
        }

        /// <summary>加载场景</summary>
        /// <param name="pSceneName"></param>
        /// <param name="pLoadCallback"></param>
        public void LoadSceneAsyc(string pSceneName, Action<bool> pLoadCallback = null)
        {
            LoadAsset(pSceneName, ELoadType.LoadSceneAsyc, pLoadCallback);
        }

        private void LoadAsset(string pAssetName, ELoadType pLoadType, Action<bool> pLoadCallback = null)
        {
            int loaderId = pAssetName.GetHashCode();
            if (mHandleDictionay.ContainsKey(loaderId))
            {
                pLoadCallback?.Invoke(true);
                return;
            }

            Loader loader = CreateLoader(loaderId, pLoadCallback);
            if (loader != null)
            {
                switch (pLoadType)
                {
                    case ELoadType.LoadAssetAsync:
                        loader.LoadAssetAsync(pAssetName);
                        break;

                    case ELoadType.LoadAssetsAsyc:
                        loader.LoadAssetsAsync(pAssetName);
                        break;

                    case ELoadType.LoadSceneAsyc:
                        loader.LoadSceneAsync(pAssetName);
                        break;
                }
            }
        }
        #endregion

        #region 加载相关/async
        public async Task<bool> LoadAssetAsync(string pAssetName)
        {
            int loaderId = pAssetName.GetHashCode();
            if (mHandleDictionay.ContainsKey(loaderId))
                return true;

            Loader loader = CreateLoader(loaderId);
            if (loader != null)
                return await loader.LoadAssetAsyncTask(pAssetName);
            else
                return false;
        }
        #endregion

        #region 卸载相关
        public void UnLoadAsset(string pAssetName)
        {
            int loaderId = pAssetName.GetHashCode();
            if (mHandleDictionay.ContainsKey(loaderId))
            {
                AsyncOperationHandle asyncOperationHandle = mHandleDictionay[loaderId];
                ManagerCollection.ResourceManager.RemoveAsset(asyncOperationHandle.Result);
                Addressables.Release(asyncOperationHandle);
                mHandleDictionay.Remove(loaderId);
            }
        }
        #endregion

        #region Loader
        private class Loader
        {
            private int mLoaderId = -1;
            private Action<bool> mLoadCallback = null;
            private AsyncOperationHandle mAsyncOperationHandle = default;

            public AsyncOperationHandle AsyncOperationHandle
            {
                get { return mAsyncOperationHandle; }
            }

            public Loader(int loaderId, Action<bool> pLoadCallback = null)
            {
                mLoaderId = loaderId;
                mLoadCallback = pLoadCallback;
            }

            #region AsyncOperationHandle.Completed
            public void LoadAssetAsync(string pAssetName)
            {
                mAsyncOperationHandle = Addressables.LoadAssetAsync<UnityEngine.Object>(pAssetName);
                mAsyncOperationHandle.Completed += LoadComplete;
            }

            public void LoadAssetsAsync(string pLabelsKey)
            {
                mAsyncOperationHandle = Addressables.LoadAssetsAsync<UnityEngine.Object>(pLabelsKey, null);
                mAsyncOperationHandle.Completed += LoadComplete;
            }

            public void LoadSceneAsync(string pSceneName)
            {
                mAsyncOperationHandle = Addressables.LoadSceneAsync(pSceneName);
                mAsyncOperationHandle.Completed += LoadComplete;
            }
            #endregion

            #region AsyncOperationHandle.Task
            public async Task<bool> LoadAssetAsyncTask(string pAssetName)
            {
                mAsyncOperationHandle = Addressables.LoadAssetAsync<UnityEngine.Object>(pAssetName);
                await mAsyncOperationHandle.Task;
                LoadComplete(mAsyncOperationHandle);
                return mAsyncOperationHandle.Status == AsyncOperationStatus.Succeeded;
            }
            #endregion

            private void LoadComplete(AsyncOperationHandle pHandle)
            {
                if (pHandle.Status == AsyncOperationStatus.Succeeded)
                {
                    ManagerCollection.LoadManager.RemoveLoader(mLoaderId);
                    ManagerCollection.LoadManager.AddHandle(mLoaderId, pHandle);
                    ManagerCollection.ResourceManager.AddAsset(pHandle.Result);
                    mLoadCallback?.Invoke(true);
                }
                else if (pHandle.Status == AsyncOperationStatus.Failed)
                {
                    Debug.LogWarning(pHandle.OperationException.Message);
                    mLoadCallback?.Invoke(false);
                }
            }
        }

        private Loader CreateLoader(int loaderId, Action<bool> pLoadCallback = null)
        {
            Loader loader = null;
            if (!mLoaderDictionary.ContainsKey(loaderId))
            {
                loader = new Loader(loaderId, pLoadCallback);
                mLoaderDictionary.Add(loaderId, loader);
            }
            return loader;
        }

        /// <summary>移除Loader</summary>
        /// <param name="loaderId">加载器Id</param>
        private void RemoveLoader(int pLoaderId)
        {
            if (mLoaderDictionary.ContainsKey(pLoaderId))
                mLoaderDictionary.Remove(pLoaderId);
        }
        #endregion

        #region Handle
        /// <summary>添加Handle</summary>
        /// <param name="pLoaderId"></param>
        /// <param name="pHandle"></param>
        public void AddHandle(int pLoaderId, AsyncOperationHandle pHandle)
        {
            if (!mHandleDictionay.ContainsKey(pLoaderId))
                mHandleDictionay.Add(pLoaderId, pHandle);
        }

        public AsyncOperationHandle GetHandle(string pAssetName)
        {
            int loaderId = pAssetName.GetHashCode();
            if (mHandleDictionay.ContainsKey(loaderId))
                return mHandleDictionay[loaderId];
            else
                return default;
        }
        #endregion
    }
}